// Copyright 2010 Alexandre Tristan St-Cyr <tristan.stcyr@gmail.com>

package com.codeplex.loadjava;

import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Type;

import com.codeplex.loadjava.model.ClassDefinition;
import com.codeplex.loadjava.model.FieldDefinition;
import com.codeplex.loadjava.model.InterfaceImplementation;
import com.codeplex.loadjava.model.JavaClass;
import com.codeplex.loadjava.model.MethodDefinition;
import com.codeplex.loadjava.model.MethodParameter;
import com.codeplex.loadjava.model.TypeReference;

class JavaModelClassAdapter extends JavaModelReaderVisitor implements ClassVisitor {
	
	private ClassDefinition currentClass;
	
	private String[] interfaces;
	
	private String packageName;
	
	public JavaModelClassAdapter(IJavaModelVisitor entityVisitor, String packageName)
	{
		super(entityVisitor);
		this.packageName = packageName;
	}
	
	@Override
	public void visit(
			int version, 
			int access, 
			String name, 
			String signature,
			String superName, 
			String[] interfaces) {
		JavaClass superClass = new JavaClass(superName);
		this.currentClass = new ClassDefinition(name, access, 
				signature, superClass, this.packageName);
		this.entityVisitor.visitClass(this.currentClass);
		this.entityVisitor.visitClass(superClass);
		this.interfaces = interfaces;
	}

	@Override
	public void visitSource(String source, String debug) {
		// TODO: Implement when source loading is added
	}

	@Override
	public void visitOuterClass(String owner, String name, String desc) {}

	@Override
	public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
		//this.visitType(desc);
		return null;
	}

	@Override
	public void visitAttribute(Attribute attr) {
		// TODO Implement when attribute loading is added
	}

	@Override
	public void visitInnerClass(
			String name, 
			String outerName,
			String innerName, 
			int access) {
		// TODO: Figure out what to do with anonymous inner classes
		// TODO: Check out access values for inner classes
		if (this.currentClass.getInternalName().equals(name) && outerName != null) {
			JavaClass outerClass = new JavaClass(outerName);
			this.entityVisitor.visitClass(outerClass);
			this.currentClass.setOuterClass(outerClass);
		}
	}

	@Override
	public FieldVisitor visitField(int access, String name, String desc,
			String signature, Object value) {
		FieldDefinition fieldDef = new FieldDefinition(this.currentClass, name, new TypeReference(desc), 
				access, signature);
		this.visitField(fieldDef);
		this.entityVisitor.visitFieldDefinition(fieldDef);
		// TODO: Return a field visitor when attributes loading is implemented
		return null;
	}
	
	@Override
	public MethodVisitor visitMethod(
			int access, 
			String methodName, 
			String methodDescriptor,
			String signature, 
			String[] exceptions) {
		MethodDefinition methodDef = new MethodDefinition(this.currentClass, methodName, methodDescriptor, 
				access, signature);
		
		// Visit the method
		this.visitMethod(methodDef);
		this.entityVisitor.visitMethodDefinition(methodDef);
		
		// Visit the thrown exceptions
		if (exceptions != null) {
			for(String exception : exceptions) {
				this.entityVisitor.visitClass(new JavaClass(exception));
			}
		}
		
		// Visit the parameters
		int parameterIndex = 0;
		for(Type parameter : Type.getArgumentTypes(methodDescriptor)) {
			TypeReference typeRef = new TypeReference(parameter);
			this.visitType(typeRef);
			MethodParameter mp = new MethodParameter(methodDef, typeRef, parameterIndex);
			this.entityVisitor.visitMethodParameter(mp);
		}
		
		return new JavaModelMethodAdapter(this.entityVisitor, methodDef);
	}

	@Override
	public void visitEnd() {
		this.entityVisitor.visitClassDefinition(this.currentClass);
		if (interfaces != null) {
			for(String interfaceName : interfaces) {
				JavaClass interfaceClass = new JavaClass(interfaceName);
				InterfaceImplementation ii = new InterfaceImplementation(
						this.currentClass, interfaceClass);
				this.entityVisitor.visitClass(interfaceClass);
				this.entityVisitor.visitInterfaceImplementations(ii);
			}
		}
	}
}
